import $ from 'jquery';
import Module from './Module';


class Hotkeys extends Module {

  static normalize(shortcut) {
    let i, j, key, keyname, keys, len;
    keys = shortcut.toLowerCase().replace(/\s+/gi, "").split("+");
    for (i = j = 0, len = keys.length; j < len; i = ++j) {
      key = keys[i];
      keys[i] = this.aliases[key] || key;
    }
    keyname = keys.pop();
    keys.sort().push(keyname);
    return keys.join("_");
  }

  _init() {
    this.id = ++this.constructor.count;
    this._map = {};
    this._delegate = typeof this.opts.el === "string" ? document : this.opts.el;
    return $(this._delegate).on(`keydown.simple-hotkeys-${this.id}`, this.opts.el, e => {
      let handler = this._getHander(e);
      return handler ? handler.call(this, e) : void 0;
    });
  }

  _getHander(e) {
    let keyname;
    let shortcut;
    if (!(keyname = this.constructor.keyNameMap[e.which])) return;
    
    shortcut = "";
    if (e.altKey) {
      shortcut += "alt_";
    }
    if (e.ctrlKey) {
      shortcut += "control_";
    }
    if (e.metaKey) {
      shortcut += "meta_";
    }
    if (e.shiftKey) {
      shortcut += "shift_";
    }
    shortcut += keyname.toLowerCase();
    return this._map[shortcut];
  }

  respondTo(subject) {
    if (typeof subject === 'string') {
      return this._map[this.constructor.normalize(subject)] != null;
    } else {
      return this._getHander(subject) != null;
    }
  }

  add(shortcut, handler) {
    this._map[this.constructor.normalize(shortcut)] = handler;
    return this;
  }

  remove(shortcut) {
    delete this._map[this.constructor.normalize(shortcut)];
    return this;
  }

  destroy() {
    $(this._delegate).off(`.simple-hotkeys-${this.id}`);
    this._map = {};
    return this;
  }
}

Hotkeys.count = 0;

Hotkeys.keyNameMap = {
  8: "Backspace",
  9: "Tab",
  13: "Enter",
  16: "Shift",
  17: "Control",
  18: "Alt",
  19: "Pause",
  20: "CapsLock",
  27: "Esc",
  32: "Spacebar",
  33: "PageUp",
  34: "PageDown",
  35: "End",
  36: "Home",
  37: "Left",
  38: "Up",
  39: "Right",
  40: "Down",
  45: "Insert",
  46: "Del",
  91: "Meta",
  93: "Meta",
  48: "0",
  49: "1",
  50: "2",
  51: "3",
  52: "4",
  53: "5",
  54: "6",
  55: "7",
  56: "8",
  57: "9",
  65: "A",
  66: "B",
  67: "C",
  68: "D",
  69: "E",
  70: "F",
  71: "G",
  72: "H",
  73: "I",
  74: "J",
  75: "K",
  76: "L",
  77: "M",
  78: "N",
  79: "O",
  80: "P",
  81: "Q",
  82: "R",
  83: "S",
  84: "T",
  85: "U",
  86: "V",
  87: "W",
  88: "X",
  89: "Y",
  90: "Z",
  96: "0",
  97: "1",
  98: "2",
  99: "3",
  100: "4",
  101: "5",
  102: "6",
  103: "7",
  104: "8",
  105: "9",
  106: "Multiply",
  107: "Add",
  109: "Subtract",
  110: "Decimal",
  111: "Divide",
  112: "F1",
  113: "F2",
  114: "F3",
  115: "F4",
  116: "F5",
  117: "F6",
  118: "F7",
  119: "F8",
  120: "F9",
  121: "F10",
  122: "F11",
  123: "F12",
  124: "F13",
  125: "F14",
  126: "F15",
  127: "F16",
  128: "F17",
  129: "F18",
  130: "F19",
  131: "F20",
  132: "F21",
  133: "F22",
  134: "F23",
  135: "F24",
  59: ";",
  61: "=",
  186: ";",
  187: "=",
  188: ",",
  190: ".",
  191: "/",
  192: "`",
  219: "[",
  220: "\\",
  221: "]",
  222: "'"
};

Hotkeys.aliases = {
  "escape": "esc",
  "delete": "del",
  "return": "enter",
  "ctrl": "control",
  "space": "spacebar",
  "ins": "insert",
  "cmd": "meta",
  "command": "meta",
  "wins": "meta",
  "windows": "meta"
};

Hotkeys.prototype.opts = { el: document };

export default (opts) => new Hotkeys(opts);

